package com.example.publice.validations;

import com.example.publice.error.ValidationException;
import com.example.util.IdCradUtil;
import com.example.util.str.StringUtils;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.regex.Pattern;

/**
 * 数据验证
 * @param <T>
 */
public abstract class DataValidator<T> {
    private Class<T> cla = null;

    private T dto = null;

    protected DataValidator(Class<T> clazz) {
        this.cla = clazz;
    }


    public interface Expression<E, T> {
        E select(T obj);
    }

    public interface Validator<T> {
        void validate();
    }


    /**
     * 初始化对象
     * @param args
     */
    public void init(List<Object> args) {
        for (Object obj : args) {
            if (obj.getClass().getTypeName().equals(this.cla.getTypeName())
                    || this.cla.isAssignableFrom(obj.getClass())) {
                this.dto = (T) obj;
            }
        }
    }

    /**
     * 抽象验证方法，成功时应返回null，否则返回一个错误信息
     *
     * @return
     */
    public abstract void validate() throws Exception;


    /**
     * boolean验证
     * @param exp
     * @param msg
     * @throws ValidationException
     */
    protected void assertTrue(Expression<Boolean, T> exp, String msg) throws ValidationException {
        if (!exp.select(this.dto)) throw new ValidationException(msg);
    }

    /**
     * 空对象验证
     * @param exp
     * @param msg
     * @throws ValidationException
     */
    protected void notNull(Expression<?, T> exp, String msg) throws ValidationException {
        if (exp.select(this.dto) == null) {
            throw new ValidationException(msg);
        }
    }

    /**
     * 空串验证
     * @param exp
     * @param msg
     * @throws ValidationException
     */
    protected void notEmpty(Expression<String, T> exp, String msg) throws ValidationException {
        if (StringUtils.isEmpty(exp.select(this.dto))) {
            throw new ValidationException(msg);
        }
    }

    private <T1, T2 extends Number> int compare(T1 a, T2 b) throws ValidationException {
        String[] integerTypes = {"byte", "short", "int", "long", "Byte", "Short", "Integer", "Long"};
        String[] floatTypes = {"float", "double", "Float", "Double"};
        String typeName = a.getClass().getTypeName();

        if (Arrays.asList(integerTypes).stream().anyMatch(x->typeName.indexOf(x)!=-1)) {
            long rslt = ((Number) a).longValue() - ((Number) b).longValue();
            if (rslt < 0) return -1;
            else if (rslt > 0) return 1;
            return 0;
        }
        if (Arrays.asList(floatTypes).stream().anyMatch(x->typeName.indexOf(x)!=-1)) {
                double rslt = ((Number) a).doubleValue() - ((Number) b).doubleValue();
                if (rslt < 0) return -1;
                else if (rslt > 0) return 1;
                return 0;
        }
        if (BigDecimal.class.getTypeName().indexOf(typeName)!=-1) {
            return ((BigDecimal) a).compareTo(new BigDecimal(((Number) b).doubleValue()));
        }
        throw new ValidationException("无效的数据类型");
    }

    /**
     * 最大值验证
     * @param exp
     * @param max
     * @param msg
     * @param <MaxType>
     * @param <E>
     * @throws ValidationException
     */
    protected <MaxType extends Number, E> void maxValue(Expression<E, T> exp, MaxType max, String msg) throws ValidationException {

        if (compare(exp.select(this.dto), max) > 0) {
            throw new ValidationException(msg);
        }
    }

    /**
     *
     * 最小值验证
     * @param exp
     * @param min
     * @param msg
     * @param <MinType>
     * @param <E>
     * @throws ValidationException
     */
    protected <MinType extends Number, E> void minValue(Expression<E, T> exp, MinType min, String msg) throws ValidationException {
        if (compare(exp.select(this.dto), min) < 0) {
            throw new ValidationException(msg);
        }
    }

    /**
     * 最大值最小值之间验证
     * @param exp
     * @param min
     * @param max
     * @param msg
     * @param <T1>
     * @param <E>
     * @throws ValidationException
     */
    protected <T1 extends Number, E> void minMaxValue(Expression<E, T> exp, T1 min, T1 max, String msg) throws ValidationException {
        E val = exp.select(this.dto);
        if (compare(val, min) < 0 || compare(val, max) > 0) {
            throw new ValidationException(msg);
        }
    }

    /**
     *最小长度验证
     * @param exp
     * @param len
     * @param msg
     * @throws ValidationException
     */
    protected void maxLen(Expression<String, T> exp, int len, String msg) throws ValidationException {
        String str = exp.select(this.dto);
        if (str != null && str.length() > len) {
            throw new ValidationException(msg);
        }
    }

    /**
     *
     * 最大长度与最小长度之间验证
     * @param exp
     * @param minLen
     * @param maxLen
     * @param msg
     * @throws ValidationException
     */
    protected void minMaxLen(Expression<String, T> exp, int minLen, int maxLen, String msg) throws ValidationException {
        String str = exp.select(this.dto);
        if (str == null) {
            if (minLen > 0) {
                throw new ValidationException(msg);
            }
        }
        if (str.length() < minLen || str.length() > maxLen) {
            throw new ValidationException(msg);
        }
    }


    /**
     *
     *大于时间验证
     * @param exp
     * @param date
     * @param msg
     * @throws ValidationException
     */
    protected void before(Expression<Date, T> exp, Date date, String msg) throws ValidationException {
        Date date2 = exp.select(this.dto);
        if (date2 == null || date2.getTime() > date.getTime()) {
            throw new ValidationException(msg);
        }
    }

    /**
     * 小于时间验证
     * @param exp
     * @param date
     * @param msg
     * @throws ValidationException
     */
    protected void after(Expression<Date, T> exp, Date date, String msg) throws ValidationException {
        Date date2 = exp.select(this.dto);
        if (date2 == null || date2.getTime() < date.getTime()) {
            throw new ValidationException(msg);
        }
    }


    /**
     * 时间段内验证
     * @param exp
     * @param beforeDate
     * @param afterDate
     * @param msg
     * @throws ValidationException
     */
    protected void dateIn(Expression<Date, T> exp, Date beforeDate, Date afterDate, String msg) throws ValidationException {
        Date date = exp.select(this.dto);
        if (date == null) {
            throw new ValidationException(msg);
        }
        if (date.getTime() < beforeDate.getTime() && date.getTime() > afterDate.getTime()) {
            throw new ValidationException(msg);
        }
    }

    /**
     *
     * 手机号验证
     * @param exp
     * @param msg
     * @throws ValidationException
     */
    protected void isPhone(Expression<String, T> exp, String msg) throws ValidationException {
        String phone = exp.select(this.dto);
        if (StringUtils.isEmpty(phone) || !Pattern.matches("^1(?:3|4|5|6|7|8)\\d{9}$", phone)) {
            throw new ValidationException(msg);
        }
    }


    /**
     * 邮箱验证
     * @param exp
     * @param msg
     * @throws ValidationException
     */
    protected void isEmail(Expression<String, T> exp, String msg) throws ValidationException {
        String email = exp.select(this.dto);
        String pattern = "^([a-zA-Z]|[0-9])(\\w|\\-)+@[a-zA-Z0-9]+\\.([a-zA-Z]{2,4})$";
        if (StringUtils.isEmpty(email) || !Pattern.matches(pattern, email)) {
            throw new ValidationException(msg);
        }
    }

    /**
     * 身份证验证
     * @param exp
     * @param msg
     * @throws ValidationException
     */
    protected void isIdCard(Expression<String, T> exp, String msg) throws ValidationException {
        String cardId = exp.select(this.dto);
        if (StringUtils.isEmpty(cardId) || !(Boolean) IdCradUtil.isValidate(cardId).get("flag")) {
            throw new ValidationException(msg);
        }
    }

    /**
     * 正则验证
     * @param regPattern
     * @param exp
     * @param msg
     * @throws ValidationException
     */
    protected void regexMatch(String regPattern, Expression<String, T> exp, String msg) throws ValidationException {
        String val = exp.select(this.dto);
        if (!Pattern.matches(regPattern, val)) throw new ValidationException(msg);
    }

    /**
     * 列表验证
     * @param exp
     * @param vals
     * @param msg
     * @param <E>
     * @throws ValidationException
     */
    protected <E> void inList(Expression<E, T> exp, E[] vals, String msg) throws ValidationException {
        if (!Arrays.asList(vals).stream().anyMatch(x->x.equals(exp.select(this.dto)))) {
            throw new ValidationException(msg);
        }
    }

    protected <E> void ifNotNull(Expression<E, T> exp, Validator<E> validator) throws Exception {
        E val = exp.select(this.dto);
        if (val == null) {
            return;
        }
        validator.validate();
    }


    protected void ifNotEmpty(Expression<String, T> exp, Validator<String> validator) throws ValidationException {
        String val = exp.select(this.dto);
        if (StringUtils.isEmpty(val)) return;
        validator.validate();
    }

    protected void ifNotNullOrWhiteSpace(Expression<String, T> exp, Validator<String> validator) throws ValidationException {
        String val = exp.select(this.dto);
        if (StringUtils.isEmpty(val)) return;
        validator.validate();
    }


}
